﻿/*

Copyright (c) 2009, Stephen Remde
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of the <organization> nor the
      names of its contributors may be used to endorse or promote products
      derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY STEPHEN REMDE ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL STEPHEN REMDE BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


*/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using mshtml;
using System.Drawing;
using System.Windows.Forms;
using System.Threading;
using System.IO;
using System.Diagnostics;

namespace WindowsFormsApplication1
{
    class VideoUtils : IDisposable
    {
        #region HTMLRendering
        [Guid("3050f669-98b5-11cf-bb82-00aa00bdce0b"),
        InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown),
        ComVisible(true),
        ComImport]
        interface IHTMLElementRender
        {
            void DrawToDC([In] IntPtr hDC);
            void SetDocumentPrinter([In, MarshalAs(UnmanagedType.BStr)] string bstrPrinterName, [In] IntPtr hDC);
        }

        private bool CaptureHtml(IHTMLDocument2 document, Graphics g)
        {
            IHTMLElement bodyElement = (IHTMLElement)document.body;

            if (null != bodyElement)
            {
                IHTMLElementRender render = (IHTMLElementRender)bodyElement;
                if (null != render)
                {
                    IntPtr memDC = g.GetHdc();
                    render.DrawToDC(memDC);
                    g.ReleaseHdc(memDC);
                    g.Flush();
                    return true;
                }
            }
            return false;
        }

        private Bitmap MakeFromHTML(string html)
        {
            string t = Path.GetTempFileName();
            string s = t + ".html";

            File.WriteAllText(s, html);
           
            System.Windows.Forms.WebBrowser wwb = new System.Windows.Forms.WebBrowser();
            wwb.Navigate("file://" + s, false);
            wwb.Width = 1280;
            wwb.Height = 720;
            
            while (wwb.ReadyState != WebBrowserReadyState.Complete)
            {
                Application.DoEvents();
                Thread.Sleep(100);
            }

            File.Delete(t);
            File.Delete(s);

            wwb.Document.Body.SetAttribute("style", "overflow: hidden");

            Bitmap b = new Bitmap(1280, 720);
            Graphics g = Graphics.FromImage(b);

            var h = new HTMLDocumentClass();
            
            
            if (!CaptureHtml((wwb.Document.DomDocument as IHTMLDocument2), g))
            {
                return b;
            }

            return null;
        }
        #endregion


        public VideoUtils(int width, int height) : this()
        {
            this.width = width;
            this.height = height;
        }

        public VideoUtils()
        {
            directory = Path.GetTempPath() + Path.GetRandomFileName();
            Directory.CreateDirectory(directory);

            this.file = directory + "{0:6}.png";
        }

        #region IDisposable Members

        ~VideoUtils()
        {
            Dispose();
        }
        

        public void Dispose()
        {
            try
            {
                Directory.Delete(directory, true);
            }
            catch
            {
            }
        }

        #endregion

        string directory;
        int currentframe;
        string file;
        int width = 1280;
        int height =720;
        int fps = 25;

        #region frames
        public void AddFrame(Bitmap b, int holdframes)
        {
            if (holdframes > 0)
            {
                string copy = string.Format(file, currentframe++);
                b.Save(copy, System.Drawing.Imaging.ImageFormat.Png);

                for (int i = 1; i < holdframes; i++)
                {
                    File.Copy(copy, string.Format(file, currentframe++));
                }
            }
        }

        public Bitmap CreateCompatibleBitmap()
        {
            return new Bitmap(width, height);
        }

        public void AddFrame(Bitmap b)
        {
            b.Save(string.Format(file, currentframe++), System.Drawing.Imaging.ImageFormat.Png);
        }

        public void Fade(string html, int inframes, int holdframes, int outframes )
        {
            Bitmap b = MakeFromHTML(html);
            Fade(b, inframes, holdframes, outframes);
        }

        public void Fade(Bitmap b, int inframes, int holdframes, int outframes)
        {
            Bitmap c = b.Clone() as Bitmap;
            Graphics g = Graphics.FromImage(c);

            for (int i = 0; i < inframes; i++)
            {
                g.DrawImageUnscaled(b, 0, 0);
                g.FillRectangle(new SolidBrush(Color.FromArgb(255 - 255 * i / inframes, Color.White)), new Rectangle(0, 0, 1280, 720));
                g.Flush();
                AddFrame(c);
            }

          
            AddFrame(b,holdframes);
           

            for (int i = 0; i < outframes; i++)
            {
                g.DrawImageUnscaled(b, 0, 0);
                g.FillRectangle(new SolidBrush(Color.FromArgb(255 * i / outframes, Color.White)), new Rectangle(0, 0, 1280, 720));
                g.Flush();
                AddFrame(c);
            }
        }
        #endregion
        
        #region encoding
        public void Encode(string output)
        {
            string opts = "\"mf://*.png\" " +
                 "-ovc lavc -lavcopts vcodec=flv:vqmin=3:vqmax=3 " +
                 "-o \"" + output + "\" " +
                 @"-of lavf -lavfopts format=flv -ofps " + fps;

            /*
            string opts_low = "\"mf://*.png\" " +
                         "-ovc lavc -lavcopts vcodec=flv:vbitrate=1000 " +
                         "-o \"" + outpath + "_low.flv\" " +
                         @"-of lavf -lavfopts format=flv -ofps 25";
            */

            var p = Process.Start(new ProcessStartInfo("mencoder.exe", opts)
            {
                WorkingDirectory = directory
            });

            p.WaitForExit();

        }
        #endregion

    }
}
